#ifndef __CONVERT_H__
#define __CONVERT_H__

////////////////////////////////////////////////////////////////////////////////
// Includes
////////////////////////////////////////////////////////////////////////////////

#include <string>
#include <cmath>
#include <algorithm>
#include <sstream>

#include "Range.hpp"
#include "SizeException.hpp"
#include "ParsingException.hpp"

////////////////////////////////////////////////////////////////////////////////
// Namespaces
////////////////////////////////////////////////////////////////////////////////

namespace Toolkit  {

////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////

// Light C style interface
#define IsBStr			Convert::IsBinaryString
#define	BStrToUInt		Convert::BinaryStringToUInt
#define	UIntToBStr		Convert::UIntToBinaryString
#define	StrToInt		Convert::StringToInt
#define	StrToIntR		Convert::StringToIntRange

/**
 *  This class provides conversion function
 */
class Convert {
	
	public:
		
		/// Check whether the given string is a binary string
		static bool IsBinaryString(const std::string & str) {
			for (unsigned int i=0; i<str.size(); ++i)
				if ((str[i] != '0') && (str[i] != '1'))
					return false;
			return true;
		}
		
		/// Convert a binary values string to an unsigned int integer
		static unsigned int BinaryStringToUInt(const std::string & str) {
			unsigned int result = 0;
			for (unsigned int i=0; i<str.size(); ++i)
				result += (str[str.size()-i-1] == '1') ? (unsigned int)std::pow(2.f,(int)i) : 0 ;
			return result;
		}
		
		/// Convert an unsigned integer to a binary values string
		static std::string UIntToBinaryString(
			const unsigned int s,
			const unsigned int length = 0
		) {
			// Convert integer into a binary string
			std::string result;
			for (unsigned int i=s; i>0; i/=2) {
				if (i%2 == 0)
					result.insert(0,"0");
				else
					result.insert(0,"1");
			}
			// Check length constraint
			if (length > 0) {
				// If string is too long, the constraint cannot be respected
				if (result.size() > length) {
					std::stringstream ss;
					ss << "Cannot convert " << s << " into a binary string of length " << length << " : minimum needed length is " << result.size();
					throw Exceptions::SizeException(ss.str());
				}
				// Otherwise fill in with zeroes
				else
					while (result.size() < length) result.insert(0,"0");
			}
			// Return string
			return result;
		}
		
		/// Convert a string to an integer
		static inline int StringToInt(const std::string & str) {
			int result = 0, sign = 1;
			if (str.empty()) throw Exceptions::ParsingException("An empty string cannot be converted to an integer");
			if (str[0] == '-') sign = -1;
			for (unsigned int i=((str[0]=='-')||(str[0]=='+'))?1:0; i<str.size(); ++i) {
				if ((str[i] < '0') || (str[i] > '9'))
					throw Exceptions::ParsingException("String " + str + " cannot be converted to an integer");
				result += (int)( ( ((int)str[str.size()-i-1]) - 48 ) * std::pow(10.f,(int)i) );
			}
			return result;
		}
		
		/// Convert a string to an integer range
		static Types::Range<int> StringToIntRange(const std::string & str, const std::string & separator) {
			using namespace std;
			Types::Range<int> result(-1,-1);
			unsigned int idx = str.find(separator,0);
			if ((idx != std::string::npos) && (idx < str.size()-1)) {
				string	begin	= str.substr(0,idx),
						end		= str.substr(idx+1,str.size()-idx-1);
				result.Begin() = StringToInt(begin);
				result.End() = StringToInt(end);
			}
			return result;
		}

};

////////////////////////////////////////////////////////////////////////////////
// Closing Namespaces
////////////////////////////////////////////////////////////////////////////////

}

////////////////////////////////////////////////////////////////////////////////

#endif

